package com.dp.mallchat.common.chat.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Pair;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dp.mallchat.common.chat.dao.ContactDao;
import com.dp.mallchat.common.chat.dao.MessageDao;
import com.dp.mallchat.common.chat.dao.RoomDao;
import com.dp.mallchat.common.chat.domain.dto.RoomBaseInfo;
import com.dp.mallchat.common.chat.domain.entity.*;
import com.dp.mallchat.common.chat.domain.enums.RoomTypeEnum;
import com.dp.mallchat.common.chat.domain.vo.resp.ChatRoomResp;
import com.dp.mallchat.common.chat.domain.vo.resp.MsgReadInfoResp;
import com.dp.mallchat.common.chat.mapper.ContactMapper;
import com.dp.mallchat.common.chat.service.ContactService;
import com.dp.mallchat.common.chat.service.adapter.ChatAdapter;
import com.dp.mallchat.common.chat.service.cache.HotRoomCache;
import com.dp.mallchat.common.chat.service.cache.RoomCache;
import com.dp.mallchat.common.chat.service.cache.RoomFriendCache;
import com.dp.mallchat.common.chat.service.cache.RoomGroupCache;
import com.dp.mallchat.common.chat.service.strategy.AbstractMsgHandler;
import com.dp.mallchat.common.chat.service.strategy.MsgHandlerFactory;
import com.dp.mallchat.common.common.domain.vo.req.CursorPageBaseReq;
import com.dp.mallchat.common.common.domain.vo.resp.CursorPageBaseResp;
import com.dp.mallchat.common.common.utils.AssertUtil;
import com.dp.mallchat.common.user.domain.user.entity.User;
import com.dp.mallchat.common.user.service.cache.UserInfoCache;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: dupeng
 * @CreateTime: 2024-07-07  14:55
 * @Description: 会话实现类
 */
@Service
@Slf4j
public class ContactServiceImpl extends ServiceImpl<ContactMapper, Contact> implements ContactService {

    @Autowired
    private MessageDao messageDao;
    @Autowired
    private ContactDao contactDao;

    @Autowired
    private RoomCache roomCache;
    @Autowired
    private RoomGroupCache roomGroupCache;
    @Autowired
    private HotRoomCache hotRoomCache;
    @Autowired
    private RoomFriendCache roomFriendCache;
    @Autowired
    private UserInfoCache userInfoCache;

    /**
     * 获取消息已读未读数
     * @param messageList 消息集
     * @param uid   uid
     * @return
     */
    @Override
    public Collection<MsgReadInfoResp> getMsgReadInfo(List<Message> messageList, Long uid) {
        //校验消息是否同属一个房间
        Map<Long, List<Message>> roomMap = messageList.stream().collect(Collectors.groupingBy(Message::getRoomId));
        AssertUtil.equal(roomMap.size(), 1, "消息集不属于同一个房间");
        //查询房间消息
        Long roomId = roomMap.keySet().iterator().next();
        Integer msgCount = contactDao.getTotalCount(roomId);

        //返回
        return messageList.stream().map(message -> {
            MsgReadInfoResp resp = new MsgReadInfoResp();
            resp.setMsgId(message.getId());
            Integer readCount = contactDao.getReadCount(roomId, message.getCreateTime(), uid);
            resp.setReadCount(readCount);
            resp.setUnReadCount(msgCount - readCount);
            return resp;
        }).collect(Collectors.toList());
    }

    /**
     * 获取会话列表
     * @param request
     * @param uid
     * @return
     */
    @Override
    public CursorPageBaseResp<ChatRoomResp> getContactPage(CursorPageBaseReq request, Long uid) {
        // 查出用户要展示的会话列表
        CursorPageBaseResp<Long> page;
        if (Objects.nonNull(uid)) {
            Double hotEnd = getCursorOrNull(request.getCursor());
            Double hotStart = null;

            // 用户基础会话
            CursorPageBaseResp<Contact> contactPage = contactDao.getContactPage(uid, request);
            List<Long> baseRoomIds = contactPage.getList().stream().map(Contact::getRoomId).collect(Collectors.toList());
            if (!contactPage.getIsLast()) { //如果不是最后一页，计算热门房间的游标
                hotStart = getCursorOrNull(contactPage.getCursor());
            }

            // 获取热门房间
            Set<ZSetOperations.TypedTuple<String>> typedTuples = hotRoomCache.getRoomRange(hotStart, hotEnd);
            List<Long> hotRoomIds = typedTuples.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull).map(Long::parseLong).collect(Collectors.toList());
            baseRoomIds.addAll(hotRoomIds);
            // ================================以上只是将普通房间和热门房间=====================================================

            // 基础会话和热门房间合并
            page = CursorPageBaseResp.init(contactPage, baseRoomIds);
        } else {// 用户未登录，只查全局房间
            CursorPageBaseResp<Pair<Long, Double>> roomCursorPage = hotRoomCache.getRoomCursorPage(request);
            List<Long> roomIds = roomCursorPage.getList().stream().map(Pair::getKey).collect(Collectors.toList());
            page = CursorPageBaseResp.init(roomCursorPage, roomIds);
        }

        // ===============开始追加附属信息======================最后组装会话信息（名称，头像，未读数等）
        List<ChatRoomResp> result = buildContactResp(uid, page.getList());
        return CursorPageBaseResp.init(page, result);
    }

    /**
     * 游标翻页
     * @param cursor 游标
     * @return
     */
    private Double getCursorOrNull(String cursor) {
        return Optional.ofNullable(cursor).map(Double::parseDouble).orElse(null);
    }

    @NotNull
    private List<ChatRoomResp> buildContactResp(Long uid, List<Long> roomIds) {
        // 表情和头像
        Map<Long, RoomBaseInfo> roomBaseInfoMap = getRoomBaseInfoMap(roomIds, uid);
        // 最后一条消息
        List<Long> msgIds = roomBaseInfoMap.values().stream().map(RoomBaseInfo::getLastMsgId).collect(Collectors.toList());
        List<Message> messages = CollectionUtil.isEmpty(msgIds) ? new ArrayList<>() : messageDao.listByIds(msgIds);
        Map<Long, Message> msgMap = messages.stream().collect(Collectors.toMap(Message::getId, Function.identity()));
        //每个房间最新一条消息的发送人信息，会话列表显示最新的用户名和消息内容
        Map<Long, User> lastMsgUidMap = userInfoCache.getBatch(messages.stream().map(Message::getFromUid).collect(Collectors.toList()));
        // 消息未读数
        Map<Long, Integer> unReadCountMap = getUnReadCountMap(uid, roomIds);
        return roomBaseInfoMap.values().stream().map(room -> {
                    ChatRoomResp resp = new ChatRoomResp();
                    RoomBaseInfo roomBaseInfo = roomBaseInfoMap.get(room.getRoomId());
                    resp.setAvatar(roomBaseInfo.getAvatar());
                    resp.setRoomId(room.getRoomId());
                    resp.setActiveTime(room.getActiveTime());
                    resp.setHot_Flag(roomBaseInfo.getHotFlag());
                    resp.setType(roomBaseInfo.getType());
                    resp.setName(roomBaseInfo.getName());
                    Message message = msgMap.get(room.getLastMsgId());
                    if (Objects.nonNull(message)) {
                        AbstractMsgHandler strategyNoNull = MsgHandlerFactory.getStrategyNoNull(message.getType());
                        resp.setText(lastMsgUidMap.get(message.getFromUid()).getName() + ":" + strategyNoNull.showContactMsg(message));
                    }
                    resp.setUnreadCount(unReadCountMap.getOrDefault(room.getRoomId(), 0));
                    return resp;
                }).sorted(Comparator.comparing(ChatRoomResp::getActiveTime).reversed())
                .collect(Collectors.toList());
    }



    //=============================获取房间基础信息=============================
    private Map<Long, RoomBaseInfo> getRoomBaseInfoMap(List<Long> roomIds, Long uid) {
        Map<Long, Room> roomMap = roomCache.getBatch(roomIds);
        // 房间根据好友和群组类型分组,   间类型 1群聊 2单聊
        Map<Integer, List<Long>> groupRoomIdMap = roomMap.values().stream().collect(Collectors.groupingBy(Room::getType,
                Collectors.mapping(Room::getId, Collectors.toList())));
        // 获取群组信息
        List<Long> groupRoomId = groupRoomIdMap.get(RoomTypeEnum.GROUP_ROOM.getType());
        Map<Long, RoomGroup> roomInfoBatch = roomGroupCache.getBatch(groupRoomId);
        // 获取好友信息
        List<Long> friendRoomId = groupRoomIdMap.get(RoomTypeEnum.FRIEND_ROOM.getType());
        Map<Long, User> friendRoomMap = getFriendRoomMap(friendRoomId, uid);

        return roomMap.values().stream().map(room -> {
            RoomBaseInfo roomBaseInfo = new RoomBaseInfo();
            roomBaseInfo.setRoomId(room.getId());
            roomBaseInfo.setType(room.getType());
            roomBaseInfo.setHotFlag(room.getHotFlag());
            roomBaseInfo.setLastMsgId(room.getLastMsgId());
            roomBaseInfo.setActiveTime(room.getActiveTime());
            //群聊设置群名称头像，单聊设置好友名称头像
            if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.GROUP_ROOM) {
                RoomGroup roomGroup = roomInfoBatch.get(room.getId());
                roomBaseInfo.setName(roomGroup.getName());
                roomBaseInfo.setAvatar(roomGroup.getAvatar());
            } else if (RoomTypeEnum.of(room.getType()) == RoomTypeEnum.FRIEND_ROOM) {
                User user = friendRoomMap.get(room.getId());
                roomBaseInfo.setName(user.getName());
                roomBaseInfo.setAvatar(user.getAvatar());
            }
            return roomBaseInfo;
        }).collect(Collectors.toMap(RoomBaseInfo::getRoomId, Function.identity()));
    }

    private Map<Long, User> getFriendRoomMap(List<Long> roomIds, Long uid) {
        if (CollectionUtil.isEmpty(roomIds)) {
            return new HashMap<>();
        }
        Map<Long, RoomFriend> roomFriendMap = roomFriendCache.getBatch(roomIds);
        Set<Long> friendUidSet = ChatAdapter.getFriendUidSet(roomFriendMap.values(), uid);
        Map<Long, User> userBatch = userInfoCache.getBatch(new ArrayList<>(friendUidSet));
        return roomFriendMap.values()
                .stream()
                .collect(Collectors.toMap(RoomFriend::getRoomId, roomFriend -> {
                    Long friendUid = ChatAdapter.getFriendUid(roomFriend, uid);
                    return userBatch.get(friendUid);
                }));
    }


    /**
     * 获取未读数
     */
    private Map<Long, Integer> getUnReadCountMap(Long uid, List<Long> roomIds) {
        if (Objects.isNull(uid)) {
            return new HashMap<>();
        }
        List<Contact> contacts = contactDao.getByRoomIds(roomIds, uid);
        return contacts.parallelStream()
                .map(contact -> Pair.of(contact.getRoomId(), messageDao.getUnReadCount(contact.getRoomId(), contact.getReadTime())))
                .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
    }
}
